use crate::json_basic::JsonNode;

/// 从token中解析数据并格式化

struct JsonDumper {
    // 当前的缩进长度, 会随着数据层级不断变换
    current_indent: usize,
    // 缩进步数
    indent_size: usize,
    // 是否把汉字编码 true表示把汉字编码成其他的编码
    escape_string: bool,
    // 结果存放地
    result: String,
}

fn translate_escape(str: &str) -> String {
    let mut result = String::new();
    for c in str.chars() {
        match c {
            '"' => result.push_str("\\\""),
            '\\' => result.push_str("\\\\"),
            '\n' => result.push_str("\\n"),
            '\r' => result.push_str("\\r"),
            '\t' => result.push_str("\\t"),
            '\u{8}' => result.push_str("\\b"),
            '\u{c}' => result.push_str("\\f"),
            '/' => result.push_str("\\/"),
            utf_char if !utf_char.is_ascii() => {
                result.push_str(&format!("\\u{:04x}", utf_char as u32))
            }
            _ => result.push(c),
        }
    }
    result
}

impl JsonDumper {
    /// 在格式化的时候用于添加空格, 每个层级的空格数是不一样的, current_indent 这个参数每次都会跟着层次变化
    fn indent(&mut self) {
        for _ in 0..self.current_indent {
            self.result.push(' ');
        }
    }

    /// 把 token 进行解析
    /// object 按照前后 {}, 里面拼接数据的格式进行填充, 可能会有其他格式的数据就进行递归
    /// array 按照前后 [], 里面拼接数据的格式进行填充, 可能会有其他格式的数据就进行递归
    /// string 按照前后 "", 里面拼接数据的格式进行填充
    /// number 按照字符串进行填充
    /// boolean 按照 true 或者 false 进行填充
    pub(crate) fn process(&mut self, json: &JsonNode) {
        match json {
            JsonNode::Object(obj) => {
                if obj.len() == 0 {
                    self.result.push_str("{}");
                    return;
                }
                self.result.push_str("{\n");
                self.current_indent += self.indent_size;
                for (idx, (key, value)) in obj.iter().enumerate() {
                    self.indent();
                    self.result.push('"');
                    self.result.push_str(key);
                    self.result.push_str("\": ");
                    self.process(value);
                    if idx < obj.len() - 1 {
                        self.result.push_str(",\n");
                    } else {
                        self.result.push('\n');
                    }
                }
                self.current_indent -= self.indent_size;
                // 这个是给最后一个 } 添加缩进的
                self.indent();
                self.result.push('}');
            }
            JsonNode::Array(arr) => {
                if arr.len() == 0 {
                    self.result.push_str("[]");
                    return;
                }
                self.result.push_str("[\n");
                self.current_indent += self.indent_size;
                for (idx, value) in arr.iter().enumerate() {
                    self.indent();
                    self.process(value);
                    if idx < arr.len() {
                        self.result.push_str(",\n");
                    } else {
                        self.result.push('\n');
                    }
                }
                self.current_indent -= self.indent_size;
                self.indent();
                self.result.push(']');
            }
            JsonNode::String(str) => {
                self.result.push('"');
                // 是否变成其他的编码
                if self.escape_string {
                    self.result.push_str(&translate_escape(str));
                } else {
                    self.result.push_str(str);
                }
                self.result.push('"');
            }
            JsonNode::Number(num) => {
                self.result.push_str(&num.to_string());
            }
            JsonNode::Boolean(bool) => {
                self.result.push_str(if *bool { "true" } else { "false" });
            }
            JsonNode::Null => {
                self.result.push_str("null");
            }
        }
    }
}

/// 进行dump格式化操作
/// 把 token 或者 JSON字符串 变成有格式的 JSON
///{
//   "is_student": true,
//   "age": 20,
//   "address": {
//     "street": "1935 Lower Mall",
//     "city": "Vancouver",
//     "postal_code": "V6T 1X1"
//   },
//   "friends": [
//     {
//       "name": "Tommy",
//       "age": 20
//     },
//     {
//       "age": 20,
//       "name": "Joe"
//     },
//   ],
//   "last name": "\u738b",
//   "courses": [
//     {
//       "name": "CPEN 212",
//       "credits": 4
//     },
//     {
//       "credits": 4,
//       "name": "CPSC 221"
//     },
//     {
//       "name": "Math 256",
//       "credits": 3
//     },
//   ],
//   "first name": "Yiran",
//   "university": "UBC",
//   "graduated": false
// }
pub fn dump_json_node(json: &JsonNode, indent: usize, escape_string: bool) -> String {
    let mut dump = JsonDumper {
        current_indent: 0,
        indent_size: indent,
        escape_string: escape_string,
        result: String::new(),
    };
    dump.process(json);
    dump.result
}

#[cfg(test)]
mod tests {
    use crate::json_parser::parse_all;
    use crate::{json_lexer};
    #[test]
    fn test_dump() {
        let json_str = "{\"first name\":\"Yiran\", \"last name\": \"王\", \"age\":20,\"is_student\":true,\"courses\":[{\"name\":\"CPEN 212\",\"credits\":4},{\"name\":\"CPSC 221\",\"credits\":4},{\"name\":\"Math 256\",\"credits\":3}],\"address\":{\"street\":\"1935 Lower Mall\",\"city\":\"Vancouver\",\"postal_code\":\"V6T 1X1\"},\"friends\":[{\"name\":\"Tommy\",\"age\":20},{\"name\":\"Joe\",\"age\":20}],\"graduated\":false, \"university\": \"UBC\"}";
        let list = json_lexer::parse_all(json_str).unwrap();
        println!("list: {:?}", list);
        let mut json = parse_all(list).unwrap();
        println!("json: {:?}", json);
        // let mut json1 = parse_json(json_str).unwrap();
        println!("{}", json.dump(2, true));
    }
}
